package com.david.appletree

import android.R
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Matrix
import android.graphics.Paint
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.graphics.drawable.StateListDrawable
import android.view.View
import android.widget.TextView
import androidx.annotation.IntRange
import androidx.exifinterface.media.ExifInterface
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation
import java.io.IOException
import java.security.MessageDigest

class BlurTransformation(@IntRange(from = 0) radius: Int) :
    BitmapTransformation() {
    private val mRadius: Int
    override fun updateDiskCacheKey(messageDigest: MessageDigest) {
        messageDigest.update(ID_BYTES)
    }

    override fun transform(
        pool: BitmapPool,
        toTransform: Bitmap,
        outWidth: Int,
        outHeight: Int
    ): Bitmap? {
        return FastBlur.doBlur(toTransform, mRadius, true)
    }

    override fun equals(o: Any?): Boolean {
        return o is BlurTransformation
    }

    override fun hashCode(): Int {
        return ID.hashCode()
    }

    companion object {
        const val DEFAULT_RADIUS = 15
        private val ID = BlurTransformation::class.java.name
        private val ID_BYTES = ID.toByteArray(CHARSET)
    }

    init {
        mRadius = radius
    }
}

class FastBlur private constructor() {
    companion object {
        fun doBlur(sentBitmap: Bitmap?, radius: Int, canReuseInBitmap: Boolean): Bitmap? {
            val bitmap: Bitmap
            if(sentBitmap==null){
                return null
            }
            bitmap = if (canReuseInBitmap) { sentBitmap } else {
                sentBitmap.copy(sentBitmap.config, true)
            }
            if (radius < 1) {
                return null
            }
            val w = bitmap.width
            val h = bitmap.height
            val pix = IntArray(w * h)
            bitmap.getPixels(pix, 0, w, 0, 0, w, h)
            val wm = w - 1
            val hm = h - 1
            val wh = w * h
            val div = radius + radius + 1
            val r = IntArray(wh)
            val g = IntArray(wh)
            val b = IntArray(wh)
            var rsum: Int
            var gsum: Int
            var bsum: Int
            var x: Int
            var y: Int
            var i: Int
            var p: Int
            var yp: Int
            var yi: Int
            var yw: Int
            val vmin = IntArray(Math.max(w, h))
            var divsum = div + 1 shr 1
            divsum *= divsum
            val dv = IntArray(256 * divsum)
            i = 0
            while (i < 256 * divsum) {
                dv[i] = i / divsum
                i++
            }
            yi = 0
            yw = yi
            val stack = Array(div) {
                IntArray(
                    3
                )
            }
            var stackpointer: Int
            var stackstart: Int
            var sir: IntArray
            var rbs: Int
            val r1 = radius + 1
            var routsum: Int
            var goutsum: Int
            var boutsum: Int
            var rinsum: Int
            var ginsum: Int
            var binsum: Int
            y = 0
            while (y < h) {
                bsum = 0
                gsum = bsum
                rsum = gsum
                boutsum = rsum
                goutsum = boutsum
                routsum = goutsum
                binsum = routsum
                ginsum = binsum
                rinsum = ginsum
                i = -radius
                while (i <= radius) {
                    p = pix[yi + Math.min(wm, Math.max(i, 0))]
                    sir = stack[i + radius]
                    sir[0] = p and 0xff0000 shr 16
                    sir[1] = p and 0x00ff00 shr 8
                    sir[2] = p and 0x0000ff
                    rbs = r1 - Math.abs(i)
                    rsum += sir[0] * rbs
                    gsum += sir[1] * rbs
                    bsum += sir[2] * rbs
                    if (i > 0) {
                        rinsum += sir[0]
                        ginsum += sir[1]
                        binsum += sir[2]
                    } else {
                        routsum += sir[0]
                        goutsum += sir[1]
                        boutsum += sir[2]
                    }
                    i++
                }
                stackpointer = radius
                x = 0
                while (x < w) {
                    r[yi] = dv[rsum]
                    g[yi] = dv[gsum]
                    b[yi] = dv[bsum]
                    rsum -= routsum
                    gsum -= goutsum
                    bsum -= boutsum
                    stackstart = stackpointer - radius + div
                    sir = stack[stackstart % div]
                    routsum -= sir[0]
                    goutsum -= sir[1]
                    boutsum -= sir[2]
                    if (y == 0) {
                        vmin[x] = Math.min(x + radius + 1, wm)
                    }
                    p = pix[yw + vmin[x]]
                    sir[0] = p and 0xff0000 shr 16
                    sir[1] = p and 0x00ff00 shr 8
                    sir[2] = p and 0x0000ff
                    rinsum += sir[0]
                    ginsum += sir[1]
                    binsum += sir[2]
                    rsum += rinsum
                    gsum += ginsum
                    bsum += binsum
                    stackpointer = (stackpointer + 1) % div
                    sir = stack[stackpointer % div]
                    routsum += sir[0]
                    goutsum += sir[1]
                    boutsum += sir[2]
                    rinsum -= sir[0]
                    ginsum -= sir[1]
                    binsum -= sir[2]
                    yi++
                    x++
                }
                yw += w
                y++
            }
            x = 0
            while (x < w) {
                bsum = 0
                gsum = bsum
                rsum = gsum
                boutsum = rsum
                goutsum = boutsum
                routsum = goutsum
                binsum = routsum
                ginsum = binsum
                rinsum = ginsum
                yp = -radius * w
                i = -radius
                while (i <= radius) {
                    yi = Math.max(0, yp) + x
                    sir = stack[i + radius]
                    sir[0] = r[yi]
                    sir[1] = g[yi]
                    sir[2] = b[yi]
                    rbs = r1 - Math.abs(i)
                    rsum += r[yi] * rbs
                    gsum += g[yi] * rbs
                    bsum += b[yi] * rbs
                    if (i > 0) {
                        rinsum += sir[0]
                        ginsum += sir[1]
                        binsum += sir[2]
                    } else {
                        routsum += sir[0]
                        goutsum += sir[1]
                        boutsum += sir[2]
                    }
                    if (i < hm) {
                        yp += w
                    }
                    i++
                }
                yi = x
                stackpointer = radius
                y = 0
                while (y < h) {

                    // Preserve alpha channel: ( 0xff000000 & pix[yi] )
                    pix[yi] =
                        -0x1000000 and pix[yi] or (dv[rsum] shl 16) or (dv[gsum] shl 8) or dv[bsum]
                    rsum -= routsum
                    gsum -= goutsum
                    bsum -= boutsum
                    stackstart = stackpointer - radius + div
                    sir = stack[stackstart % div]
                    routsum -= sir[0]
                    goutsum -= sir[1]
                    boutsum -= sir[2]
                    if (x == 0) {
                        vmin[y] = Math.min(y + r1, hm) * w
                    }
                    p = x + vmin[y]
                    sir[0] = r[p]
                    sir[1] = g[p]
                    sir[2] = b[p]
                    rinsum += sir[0]
                    ginsum += sir[1]
                    binsum += sir[2]
                    rsum += rinsum
                    gsum += ginsum
                    bsum += binsum
                    stackpointer = (stackpointer + 1) % div
                    sir = stack[stackpointer]
                    routsum += sir[0]
                    goutsum += sir[1]
                    boutsum += sir[2]
                    rinsum -= sir[0]
                    ginsum -= sir[1]
                    binsum -= sir[2]
                    yi += w
                    y++
                }
                x++
            }
            bitmap.setPixels(pix, 0, w, 0, 0, w, h)
            return bitmap
        }

        /**
         * 给 [View] 设置高斯模糊背景图片
         *
         * @param context
         * @param bkg
         * @param view
         */
        fun blur(context: Context, bitmap: Bitmap?, view: View) {
            var bkg = bitmap
            val startMs = System.currentTimeMillis()
            val radius = 15f
            val scaleFactor = 8f
            //放大到整个view的大小
            bkg = bkg?.let {
                DrawableProvider.getReSizeBitmap(
                    it,
                    view.measuredWidth.toFloat(),
                    view.measuredHeight.toFloat()
                )
            }
            var overlay = Bitmap.createBitmap(
                (view.measuredWidth / scaleFactor).toInt(),
                (view.measuredHeight / scaleFactor).toInt(),
                Bitmap.Config.ARGB_8888
            )
            overlay?.let {
                val canvas = Canvas(it)
                canvas.translate(-view.left / scaleFactor, -view.top / scaleFactor)
                canvas.scale(1 / scaleFactor, 1 / scaleFactor)
                val paint = Paint()
                paint.flags = Paint.FILTER_BITMAP_FLAG
                if (bkg != null) {
                    canvas.drawBitmap(bkg, 0f, 0f, paint)
                }
                overlay = doBlur(it, radius.toInt(), true)
                view.setBackgroundDrawable(BitmapDrawable(context.resources, overlay))
            }

        }

        /**
         * 将 [Bitmap] 高斯模糊并返回
         *
         * @param bkg
         * @param width
         * @param height
         * @return
         */
        fun blurBitmap(bkg: Bitmap?, width: Int, height: Int): Bitmap? {
            var bkg = bkg
            val startMs = System.currentTimeMillis()
            val radius = 15f //越大模糊效果越大
            val scaleFactor = 8f
            //放大到整个view的大小
            bkg = DrawableProvider.getReSizeBitmap(bkg!!, width.toFloat(), height.toFloat())
            var overlay = Bitmap.createBitmap(
                (width / scaleFactor).toInt(),
                (height / scaleFactor).toInt(),
                Bitmap.Config.ARGB_8888
            )
            val canvas = Canvas(overlay)
            canvas.scale(1 / scaleFactor, 1 / scaleFactor)
            val paint = Paint()
            paint.flags = Paint.FILTER_BITMAP_FLAG
            if (bkg != null) {
                canvas.drawBitmap(bkg, 0f, 0f, paint)
            }
            overlay = doBlur(overlay, radius.toInt(), true)
            return overlay
        }
    }

    init {
        throw IllegalStateException("you can't instantiate me!")
    }
}

class DrawableProvider private constructor() {
    companion object {
        /**
         * 获得选择器
         *
         * @param normalDrawable
         * @param pressDrawable
         * @return
         */
        fun getStateListDrawable(normalDrawable: Drawable?, pressDrawable: Drawable?): Drawable {
            val stateListDrawable = StateListDrawable()
            stateListDrawable.addState(intArrayOf(R.attr.state_checked), pressDrawable)
            stateListDrawable.addState(intArrayOf(), normalDrawable)
            return stateListDrawable
        }

        /**
         * 将 TextView/RadioButton 中设置的 drawable 动态的缩放
         *
         * @param percent
         * @param tv
         * @return
         */
        fun getScaleDrawableForRadioButton(percent: Float, tv: TextView): Drawable? {
            val compoundDrawables = tv.compoundDrawables
            var drawable: Drawable? = null
            for (d in compoundDrawables) {
                if (d != null) {
                    drawable = d
                }
            }
            return getScaleDrawable(percent, drawable)
        }

        /**
         * 将 TextView/RadioButton 中设置的 drawable 动态的缩放
         *
         * @param tv
         * @return
         */
        fun getScaleDrawableForRadioButton2(width: Float, tv: TextView): Drawable? {
            val compoundDrawables = tv.compoundDrawables
            var drawable: Drawable? = null
            for (d in compoundDrawables) {
                if (d != null) {
                    drawable = d
                }
            }
            return getScaleDrawable2(width, drawable)
        }

        /**
         * 传入图片,将图片按传入比例缩放
         *
         * @param percent
         * @return
         */
        fun getScaleDrawable(percent: Float, drawable: Drawable?): Drawable? {
            drawable?.setBounds(
                0, 0, (drawable.intrinsicWidth * percent + 0.5f).toInt(),
                (drawable.intrinsicHeight * percent + 0.5f).toInt()
            )
            return drawable
        }

        /**
         * 传入图片,将图片按传入宽度和原始宽度的比例缩放
         *
         * @param width
         * @return
         */
        fun getScaleDrawable2(width: Float, drawable: Drawable?): Drawable? {
            drawable?.let {
                val percent = width * 1.0f / it.intrinsicWidth
                return getScaleDrawable(percent, it)
            }
            return null
        }

        /**
         * 设置左边的drawable
         *
         * @param tv
         * @param drawable
         */
        fun setLeftDrawable(tv: TextView, drawable: Drawable?) {
            tv.setCompoundDrawables(drawable, null, null, null)
        }

        /**
         * 改变Bitmap的长宽
         *
         * @param bitmap
         * @return
         */
        fun getReSizeBitmap(bitmap: Bitmap, targetWidth: Float, targetheight: Float): Bitmap? {
            var returnBm: Bitmap? = null
            val width = bitmap.width
            val height = bitmap.height
            val matrix = Matrix()
            matrix.postScale(targetWidth / width, targetheight / height) //长和宽放大缩小的比例
            try {
                returnBm = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true)
            } catch (e: Exception) {
                e.printStackTrace()
            }
            if (returnBm == null) {
                returnBm = bitmap
            }
            if (bitmap != returnBm) {
                bitmap.recycle()
            }
            return returnBm
        }

        /**
         * 将图片按照某个角度进行旋转
         *
         * @param bm     需要旋转的图片
         * @param degree 旋转角度
         * @return 旋转后的图片
         */
        fun rotateBitmapByDegree(bm: Bitmap, degree: Int): Bitmap? {
            var returnBm: Bitmap? = null

            // 根据旋转角度，生成旋转矩阵
            val matrix = Matrix()
            matrix.postRotate(degree.toFloat())
            try {
                // 将原始图片按照旋转矩阵进行旋转，并得到新的图片
                returnBm = Bitmap.createBitmap(bm, 0, 0, bm.width, bm.height, matrix, true)
            } catch (e: OutOfMemoryError) {
                e.printStackTrace()
            }
            if (returnBm == null) {
                returnBm = bm
            }
            if (bm != returnBm) {
                bm.recycle()
            }
            return returnBm
        }

        /**
         * 读取图片的旋转的角度
         *
         * @param path 图片绝对路径
         * @return 图片的旋转角度
         */
        fun getBitmapDegree(path: String?): Int {
            var degree = 0
            if(path==null){
                return degree
            }
            try {
                // 从指定路径下读取图片，并获取其EXIF信息
                val exifInterface = ExifInterface(path)
                // 获取图片的旋转信息
                val orientation = exifInterface.getAttributeInt(
                    ExifInterface.TAG_ORIENTATION,
                    ExifInterface.ORIENTATION_NORMAL
                )
                when (orientation) {
                    ExifInterface.ORIENTATION_ROTATE_90 -> degree = 90
                    ExifInterface.ORIENTATION_ROTATE_180 -> degree = 180
                    ExifInterface.ORIENTATION_ROTATE_270 -> degree = 270
                    else -> {
                    }
                }
            } catch (e: IOException) {
                e.printStackTrace()
            }
            return degree
        }
    }

    init {
        throw java.lang.IllegalStateException("you can't instantiate me!")
    }
}